package com.vpx.server.service.system.impl;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONObject;
import com.vpx.server.bean.enums.ProjectConfigTypeEnum;
import com.vpx.server.service.system.SqlExtService;
import com.vpx.server.service.system.XmlFlowConfigService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class XmlFlowConfigServiceImpl implements XmlFlowConfigService {
    @Resource
    SqlExtService sqlExtService;
    public  int instanIndex = 0;
    public  int branchIndex = 0;
    public  int stepIndex = 0;

    @Override
    public void init() {
       instanIndex = 0;
       branchIndex = 0;
       stepIndex = 0;
    }

    @Override
    public void createFlowTestConfigXml(JSONObject project,JSONObject flowInfo, Map<String, String> projectMap) {
        String projectName = project.getStr("project_name");
        String projectPath = project.getStr("project_path");
        String flowName = flowInfo.getStr("name");
        String flowId = flowInfo.getStr("id");
        String dirPath = projectPath + File.separator + projectName + File.separator + flowName + File.separator + "TestXML";
        String projectId = project.getStr("id");
        Document document = XmlUtil.createXml();
        Element element = getTestConfigXML(document,flowId,flowName, projectMap,branchIndex,stepIndex);
        document.appendChild(element);

        //创建文件夹
        File directory = new File(dirPath);
        if (!directory.exists()) {
            if (!directory.mkdirs()) {

            }
        }
        XmlUtil.toFile(document,dirPath + File.separator + flowName + "_Test.xml");
    }

    Element getTestConfigXML(Document document,String flowId,String flowName, Map<String, String> projectMap,int branchIndex, int stepIndex){
        Map<String,Integer> nodeIndexMap = new HashMap<>();
        Element element = document.createElement("ProjectTest");
        element.setAttribute("Index",flowName);
        element.setAttribute("version","ADE3.3.4.20230522_Release");
        element.setAttribute("NodeEnable","true");
        element.setAttribute("EXISTQUOTE","true");

        //缓存当前的所有节点
        List<JSONObject> flowNodeInfos = sqlExtService.findListObject("select bi.*,ct.template_code from c_base_info bi,c_template ct where  bi.parent_all_id like '%"+flowId+"%' and bi.template_id = ct.id order by bi.num asc", JSONObject.class);
        //缓存当前测试路程的属性节点
        List<JSONObject> flowNodeInfoAttrs = sqlExtService.findListObject("select * from c_base_info_attr where base_info_id in (select bi.id from c_base_info bi where  bi.parent_all_id like '%"+flowId+"%')", JSONObject.class);
        //获取所有的测试用例
        List<JSONObject> instans = new ArrayList<>();
        Map<String,List<JSONObject>> instansIdAndBranchObj = new HashMap<>();
        Map<String,List<JSONObject>> branchIdAndStepObj = new HashMap<>();
        Map<String,JSONObject> nodeIdInfoMap = new HashMap<>();
        for(JSONObject node:flowNodeInfos) {
            String node_type = node.getStr("node_type");
            if("1".equals(node_type)) {
                //测试用例
                instans.add(node);
            } else if("8".equals(node_type)) {
                //8分支 按照用例分组
                String instance_id = node.getStr("instance_id");
                List<JSONObject> branchs = instansIdAndBranchObj.get(instance_id);
                if(branchs == null){
                    branchs = new ArrayList<>();
                }
                branchs.add(node);
                instansIdAndBranchObj.put(instance_id, branchs);

            } else if("2".equals(node_type)) {
                //流程步 按照分支分组
                String branch_id = node.getStr("branch_id");
                List<JSONObject> steps = branchIdAndStepObj.get(branch_id);
                if(steps == null) {
                    steps = new ArrayList<>();
                }
                steps.add(node);
                branchIdAndStepObj.put(branch_id, steps);
            }
            nodeIdInfoMap.put(node.getStr("id"), node);
        }
        //按照 base_info_id 分组
        Map<String, List<JSONObject>> flowNodeInfoAttrsMap = CollStreamUtil.groupBy(flowNodeInfoAttrs, item -> item.getStr("base_info_id"), Collectors.toList());
        instans = sortInstance(instans, flowId);

        for(int i = 0;i < instans.size();i++) {
            instanIndex = (i + 1);
            nodeIndexMap.put(instans.get(i).getStr("id"), instanIndex);
            //添加分支节点 获取所有的分支
            List<JSONObject> branchs = instansIdAndBranchObj.get(instans.get(i).getStr("id"));
            branchs = sortStep(branchs, instans.get(i).getStr("id"));

            if(branchs!=null) {
                for (int j = 0; j < branchs.size(); j++) {
                    branchIndex = branchIndex + 1;
                    nodeIndexMap.put(branchs.get(j).getStr("id"), branchIndex);

                    List<JSONObject> steps = branchIdAndStepObj.get(branchs.get(j).getStr("id"));
                    steps = sortStep(steps, branchs.get(j).getStr("id"));
                    //流程步与ID索引
                    if (steps != null) {
                        for(int k = 0;k < steps.size(); k++) {
                            stepIndex = stepIndex + 1;
                            nodeIndexMap.put(steps.get(k).getStr("id"),stepIndex);
                        }
                    }
                }
            }
        }

        for(int i = 0;i < instans.size();i++) {
            element.appendChild(getTestProcedure(document,instans.get(i), flowNodeInfoAttrsMap, instansIdAndBranchObj, branchIdAndStepObj, projectMap,nodeIndexMap,nodeIdInfoMap));
        }
        return element;
    }

    Element getTestProcedure(Document document, JSONObject instance, Map<String, List<JSONObject>> flowNodeInfoAttrsMap,
                             Map<String,List<JSONObject>> instansIdAndBranchObj,Map<String,List<JSONObject>> branchIdAndStepObj,
                             Map<String, String> projectMap,Map<String,Integer> nodeIndexMap,Map<String, JSONObject> nodeIdInfoMap) {
        Element element = document.createElement("TestProcedure");
        String template_code = instance.getStr("template_code");
        element.setAttribute("TemplateType","TestProcedure");
        element.setAttribute("ProcedureType","TestProcedure");
        element.setAttribute("Index", nodeIndexMap.get(instance.getStr("id")).intValue()+"");
        String instancNotes = instance.getStr("notes");
        if("true".equals(instancNotes)) {
            //当前节点被注释
            element.setAttribute("NodeEnable","false");
        } else {
            element.setAttribute("NodeEnable","true");
        }

        element.setAttribute("EXISTQUOTE","true");

        String instanId = instance.getStr("id");
        List<JSONObject> instanceAttrs = flowNodeInfoAttrsMap.get(instanId);
        sortStepAttr(instanceAttrs);
        //添加属性节点
        for(JSONObject instanceAttr:instanceAttrs) {
            Element element1 = document.createElement(instanceAttr.getStr("attr_code"));
            element1.setTextContent(instanceAttr.getStr("attr_content"));
            element.appendChild(element1);
        }
        //添加分支节点 获取所有的分支
        List<JSONObject> branchs = instansIdAndBranchObj.get(instanId);
        branchs = sortStep(branchs, instanId);

        if(branchs!=null) {
            for (int i = 0; i < branchs.size(); i++) {
//                branchIndex = branchIndex + 1;
//                nodeIndexMap.put(branchs.get(i).getStr("id"), branchIndex);
                //添加分支节点
                element.appendChild(getTestCase(document, branchs.get(i),  flowNodeInfoAttrsMap, branchIdAndStepObj, instancNotes, projectMap,nodeIndexMap,nodeIdInfoMap));
            }
        }
        return element;
    }
    Element getTestCase(Document document, JSONObject branch, Map<String, List<JSONObject>> flowNodeInfoAttrsMap,
                        Map<String,List<JSONObject>> branchIdAndStepObj, String instancNotes, Map<String, String> projectMap,Map<String,Integer> nodeIndexMap,Map<String, JSONObject> nodeIdInfoMap) {
        Element element = document.createElement("TestCase");
        String template_code = branch.getStr("template_code");
        element.setAttribute("TemplateType","TestCase");
        element.setAttribute("CaseType","TestCase");
        element.setAttribute("Index", nodeIndexMap.get(branch.getStr("id")).intValue()+"");
        element.setAttribute("NodeEnable","true");
        String branchNotes = branch.getStr("notes");
        if("true".equals(instancNotes)) {
            //当前节点被注释
            element.setAttribute("NodeEnable","false");
        } else {
            if("true".equals(branchNotes)) {
                element.setAttribute("NodeEnable","false");
            } else {
                element.setAttribute("NodeEnable","true");
            }
        }
        element.setAttribute("EXISTQUOTE","true");
        String branchId = branch.getStr("id");
        List<JSONObject> branchAttrs = flowNodeInfoAttrsMap.get(branchId);
        sortStepAttr(branchAttrs);
        //添加属性节点
        for(JSONObject branchAttr:branchAttrs) {
            Element element1 = document.createElement(branchAttr.getStr("attr_code"));
            element1.setTextContent(branchAttr.getStr("attr_content"));
            element.appendChild(element1);
        }
        //添加当前分支下的流程步 获取所有流程步
        List<JSONObject> steps = branchIdAndStepObj.get(branchId);
        steps = sortStep(steps, branchId);
        //流程步与ID索引

        if (steps != null) {
            for(int i = 0;i < steps.size(); i++) {
//                stepIndex = stepIndex + 1;
//                nodeIndexMap.put(steps.get(i).getStr("id"),stepIndex);
                element.appendChild(getStep(document, steps.get(i),  stepIndex, flowNodeInfoAttrsMap,instancNotes, branchNotes, projectMap,nodeIndexMap,nodeIdInfoMap));
            }
        }
        return element;
    }
    Element getStep(Document document, JSONObject step, int stepIndex,
                    Map<String, List<JSONObject>> flowNodeInfoAttrsMap,
                    String instancNotes, String branchNotes, Map<String, String> projectMap,Map<String,Integer> nodeIndexMap,Map<String, JSONObject> nodeIdInfoMap) {
        Element element = document.createElement("TestStep");
        String template_code = step.getStr("template_code");
        element.setAttribute("TemplateType",template_code);
        element.setAttribute("StepType",template_code);
        element.setAttribute("Index", nodeIndexMap.get(step.getStr("id")).intValue()+"");
        String stepNotes = step.getStr("notes");
        String existQuote = "false";
        if("true".equals(instancNotes)) {
            //当前节点被注释
            element.setAttribute("NodeEnable","false");
        } else {
            if("true".equals(branchNotes)) {
                element.setAttribute("NodeEnable","false");
            } else {
                if("true".equals(stepNotes)) {
                    element.setAttribute("NodeEnable","false");
                } else {
                    element.setAttribute("NodeEnable","true");
                }
            }
        }
        String stepId = step.getStr("id");
        //获取当前节点所有属性
        List<JSONObject> stepAttrs = flowNodeInfoAttrsMap.get(stepId);
        sortStepAttr(stepAttrs);
        if(stepAttrs!=null){
            for(JSONObject stepAttr:stepAttrs) {
                Element attr = document.createElement(stepAttr.getStr("attr_code"));
                //get set方式
                String is_get_set = stepAttr.getStr("is_get_set", "");
                if(StringUtils.isNotBlank(is_get_set)) {
                    attr.setAttribute("Mode", is_get_set);
                }
                //是否引用
                String is_quote = stepAttr.getStr("is_quote", "");
                if(StringUtils.isNotBlank(is_quote)) {
                    if("1".equals(is_quote)) {
                        attr.setAttribute("CanRef", "true");
                    }
                }
                //(一般协议为空，1、硬件配置 2、总线配置 3、协议配置 4、共享配置 5、观察者配置 ) 对应 xml的编码  0  一般类型  1 硬件类型  2  协议类型  3 总线类型 4 观察者类型 5 共享类型
                attr.setAttribute("Type", stepAttr.getStr("data_type"));
                String protocol_type = stepAttr.getStr("protocol_type", "");
                String xmlProType = "0";
                switch (protocol_type) {
                    case "":
                        xmlProType = "0";
                        break;
                    case "1":
                        xmlProType = "1";
                        break;
                    case "3":
                        xmlProType = "2";
                        break;
                    case "2":
                        xmlProType = "3";
                        break;
                    case "5":
                        xmlProType = "4";
                        break;
                    case "4":
                        xmlProType = "5";
                        break;
                    default:
                }
                attr.setAttribute("StepProType", xmlProType);
                String attr_content = stepAttr.getStr("attr_content","");
                if("0".equals(xmlProType)) {
                    //一般协议
                    //判断当前内容是否引用
                    if(match(attr_content)) {
                        existQuote = "TRUE";
                        //引用 #nodeName#nodeId#attrName#attrCode#
                        //    #测试引用#f2d5fc4d-5ca8-4baa-a9f7-ba44b4a84b33#引用数组#ce#19#
                        String[] contents =  attr_content.split("#");
                        JSONObject quotoJsonObject = nodeIdInfoMap.get(contents[2]);
                        if(contents.length == 6) {
                            if(quotoJsonObject != null) {
                                attr.setTextContent("!"+nodeIndexMap.get(quotoJsonObject.getStr("instance_id"))+"@"+nodeIndexMap.get(quotoJsonObject.getStr("branch_id"))+"#"+nodeIndexMap.get(contents[2]) + "$"+ contents[4] + "_" + contents[5] + "$"+"11");
                            } else {
                                attr.setTextContent("");
                            }
                        } else {
                            //!用例index@分支index#流程步index$字段编码$
                            attr.setTextContent("!"+nodeIndexMap.get(quotoJsonObject.getStr("instance_id"))+"@"+nodeIndexMap.get(quotoJsonObject.getStr("branch_id"))+"#"+nodeIndexMap.get(contents[2]) + "$"+ contents[4] + "$"+"11");
                        }

                    } else {
                        attr.setTextContent(attr_content);
                    }
                } else {
                    //设置协议编码 数据库中存储的是 c_project_config ID 要转换成相应的uuid 及 config_code
                    attr.setTextContent(projectMap.get(attr_content));
                }

                element.appendChild(attr);
            }
        }

        element.setAttribute("EXISTQUOTE",existQuote);
        return element;
    }

    private boolean match(String content) {
        String regex  ="#.+#.+#.+#.+#";
        return ReUtil.isMatch(regex, content);
    }
    private List<JSONObject> sortStepAttr(List<JSONObject> list) {
        Collections.sort(list, new Comparator<JSONObject>() {
            @Override
            public int compare(JSONObject o1, JSONObject o2) {

                return Integer.compare(o1.getInt("sort_num"),o2.getInt("sort_num"));
            }
        });
        return list;
    }
    private List<JSONObject> sortStep(List<JSONObject> list,String branchId) {
        Map<String,JSONObject> map=new HashMap<String,JSONObject>();
        for(int i = 0; i < list.size(); i++){
            map.put(list.get(i).getStr("parent_id"),list.get(i));
        }
        List<JSONObject>  result=new ArrayList<JSONObject>();
        sortStepMap(result, branchId, map);
        return result;
    }
    private void sortStepMap(List<JSONObject>  result, String parentId,Map<String,JSONObject> nodeMap) {
        JSONObject node = nodeMap.get(parentId);
        if(node != null) {
            result.add(node);
            sortStepMap(result, node.getStr("id"),nodeMap);
        }

    }

    /**
     * 用例排序
     * @param instances
     * @param flowId
     * @return
     */
    private List<JSONObject> sortInstance(List<JSONObject>  instances, String flowId) {
        if (instances != null && instances.size() > 0) {
            List<JSONObject> result = new ArrayList<>();
            List<JSONObject> firstInstance = new ArrayList<>();
            Map<String, JSONObject> otherParentIdMap = new HashMap<>();
            for (JSONObject instance: instances) {
                if (flowId.equals( instance.getStr("parent_id"))) {
                    firstInstance.add(instance);
                } else {
                    otherParentIdMap.put( instance.getStr("parent_id"), instance);
                }
            }
            firstInstance = sortInstanceFirst(firstInstance);
            result.addAll(firstInstance);
            for(JSONObject instance: firstInstance) {
                if (otherParentIdMap.get(instance.getStr("id")) != null) {
                    List<JSONObject> children = new ArrayList<>();
                    sortFirstMap(children, instance.getStr("id"), otherParentIdMap);
                    result.addAll(children);
                }
            }

            return result;
        } else {
            return instances;
        }
    }
    private List<JSONObject> sortInstanceFirst(List<JSONObject> list) {
        Collections.sort(list, new Comparator<JSONObject>() {
            @Override
            public int compare(JSONObject o1, JSONObject o2) {
                long x = Long.parseLong(o1.getStr("num"));
                long y = Long.parseLong(o2.getStr("num"));
                return (x < y) ? -1 : ((x == y) ? 0 : 1);
            }
        });
        return list;
    }



    private void sortFirstMap(List<JSONObject>  result, String parentId,Map<String,JSONObject> nodeMap) {
        JSONObject node = nodeMap.get(parentId);
        if(node != null) {
            result.add(node);
            sortFirstMap(result, node.getStr("id"),nodeMap);
        }

    }
}
